#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


#if defined(___SYS_I486)
#if !defined(___SYS_I386)
#define ___SYS_I386
#endif
#endif
#if defined(___SYS_I386)
#if !defined(___LSB)
#define ___LSB
#endif
#endif
#if !defined(___LSB) && !defined(___MSB)
#error Missing byteorder
#endif
#if defined(___LSB) && defined(___MSB)
#error Invalid byteorder
#endif


typedef void xx;
typedef   signed char  I1;
typedef unsigned char  N1;
typedef   signed short int I2;
typedef unsigned short int N2;
typedef   signed long  int I4;
typedef unsigned long  int N4;
typedef xx *Ptr;
typedef N1 *Str;


#define Bit(n)           (1ul<<(n))
#define Typ(t,v)         ((t)(v))
#define mp1(p,n)         Typ(N1 *,Typ(N1 *,p)+(n))
#define mp2(p,n)         Typ(N2 *,Typ(N1 *,p)+(n))
#define mp4(p,n)         Typ(N4 *,Typ(N1 *,p)+(n))
#define mn1(p,n)         (*mp1(p,n))
#define mn2(p,n)         (*mp2(p,n))
#define mn4(p,n)         (*mp4(p,n))
#define mn1x(p,n)        mx1(mn1(p,n))
#define mn2x(p,n)        mx2(mn2(p,n))
#define mn4x(p,n)        mx4(mn4(p,n))
#if defined(___MSB)
#define mx1(d)           Typ(N1,d)
#define mx2(d)           Typ(N2,d)
#define mx4(d)           Typ(N4,d)
#else
#define mx1(d)              (Typ(N1,d))
#define mx2(d)           sw2(Typ(N2,d))
#define mx4(d)           sw4(Typ(N4,d))
#endif
#define msw2(d)          ((Typ(N2,d)<<8)|(Typ(N2,d)>>8))
#define msw4(d)          (((Typ(N4,d))>>24)| \
                          ((Typ(N4,d)&0x00FF0000ul)>>8)| \
                          ((Typ(N4,d)&0x0000FF00ul)<<8)| \
                          ((Typ(N4,d))<<24))
#define BEG              do {
#define END              } while (0)
#define ERR0(s)          BEG fprintf(stderr,s "\n"); \
                         fflush(stdout); fflush(stderr); \
                         return (-1); END
#define ERR1(s,n1)       BEG fprintf(stderr,s "\n",n1); \
                         fflush(stdout); fflush(stderr); \
                         return (-1); END
#define ERR2(s,n1,n2)    BEG fprintf(stderr,s "\n",n1,n2); \
                         fflush(stdout); fflush(stderr); \
                         return (-1); END

#define fCC              0x0F
#define _fZR             0
#define _fSN             1
#define _fCY             2
#define _fOF             3
#define _fSGN            4
#define _fFLG            15
#define fZR              Bit(_fZR)
#define fSN              Bit(_fSN)
#define fCY              Bit(_fCY)
#define fOF              Bit(_fOF)
#define fSGN             Bit(_fSGN)
#define fFLG             Bit(_fFLG)
#define zzFLG            flg&=~fCC
#define mFLz(d)          BEG if (!(d)) flg|=fZR; END
#define mFLs(d)          BEG if (Typ(I4,d)<0) flg|=fSN; END
#define mFLc(d)          BEG if (Typ(I4,d)<0) flg|=fCY; END
#define mFLo(d)          BEG if (Typ(I4,d)<0) flg^=fOF; END
#define mFLso(d)         BEG if (Typ(I4,d)<0) { flg|=fSN; flg^=fOF; } END
#define mFLco(d1,d2)     BEG if (Typ(N4,d1)<Typ(N4,d2)) { flg|=fCY; flg^=fOF; } END
#define mFLzs(d)         BEG mFLz(d); mFLs(d); END
#define mFLzso(d)        BEG mFLz(d); mFLso(d); END

#define rFL              0x00u
#define rIP              0x04u
#define rSP              0x08u
#define rBP              0x0Cu
#define rD1              0x10u
#define rD2              0x14u
#define rCNT             0x18u
#define rTIM             0x1Cu
#define rMAX             0x20u
#define rLEN             0x24u
#define rBEG             0x28u
#define rBLK             0x2Cu
#define gREG(r)          mn4x(mem,r)
#define sREG(r,v)        mn4(mem,r)=mx4(v)
#define gREG1(r)         mn1(mem,r)
#define sREG1(r,v)       mn1(mem,r)=(v)
#define gFL              gREG(rFL)
#define gIP              mp1(mem,gREG(rIP))
#define gSP              mp4(mem,gREG(rSP))
#define gBP              mp4(mem,gREG(rBP))
#define sFL(flg)         sREG(rFL,flg)
#define sIP(pip)         sREG(rIP,Typ(N1 *,pip)-mem)
#define sSP(psp)         sREG(rSP,Typ(N1 *,psp)-mem)
#define sBP(pbp)         sREG(rBP,Typ(N1 *,pbp)-mem)
#define mDif(p1,p2)      Typ(N4,Typ(N1 *,p1)-Typ(N1 *,p2))
#define mMem(p1,p2,n)    (Typ(N1 *,(p2)+(n))>Typ(N1 *,p1)+ml || \
                          Typ(N1 *,(p2))<Typ(N1 *,p1)+REGLEN)
#define mStk(n,s)        if (mMem(mem,psp,n)) \
                           ERR2("Error: %08lX: " s ": %08lX", \
                           mDif(pip,mem),mDif(psp,mem))


#define REGLEN 256ul
#define MEMLEN 65536ul


static inline N2 sw2(N2 d)
{ return (msw2(d));
}
static inline N4 sw4(N4 d)
{ return (msw4(d));
}
static Ptr MemGet(N4 len)
{ if (!len) return (NULL);
  return (malloc(len));
}
static xx MemRel(Ptr p)
{ if (p) free(p);
}
N4 dump(N1 *mem);

I4 sys(N1 *mem,N4 ml,N1 *pip,N4 **_psp)
{ N4 *psp=*_psp,n1;

  n1=mx4(psp[0]);
  switch (n1)
  { case 0: // StdGet(xx):N4
      n1=(I4)fgetc(stdin);
      psp[0]=mx4(n1);
      break;
    case 1: // StdPut(N1 cc):xx
      mStk(2,"SYS/StdPut/STK");
      n1=mx4(psp[1]);
      fputc((N1)n1,stdout);
      fflush(stdout);
      *_psp+=2; break;
    case 2: // StdErr(N1 cc):xx
      mStk(2,"SYS/StdErr/STK");
      n1=mx4(psp[1]);
      fputc((N1)n1,stderr);
      fflush(stderr);
      *_psp+=2; break;
    case 3: // Halt
      ERR1("Error: %08lX: SYS/Halt",mDif(pip,mem));
    default:
      ERR2("Error: %08lX: SYS/%08lX",mDif(pip,mem),n1);
  }
  return (0);
}

int main(int argc,char **argv)
{ N4 n1,n2,n3; I4 i1,i2;
  N1 run=1; N4 flg,cnt;
  N1 *mem,*pip; N4 *psp,*pbp;
  N4 ml=MEMLEN;

#if defined(___OS_WIN)
  setmode(fileno(stdin),O_BINARY);
  setmode(fileno(stdout),O_BINARY);
  setmode(fileno(stderr),O_BINARY);
#endif

  if (!(mem=MemGet(ml)))
    ERR0("Error: Mem: Len");
  for (pip=mem,n1=0;n1<ml;++n1)
    *pip++=0;
  // Init
  flg=0; cnt=-1;
  pip=mp1(mem,REGLEN);
  psp=mp4(mem,ml);
  pbp=mp4(mem,ml);
  sFL(flg); sREG(rCNT,cnt);
  sIP(pip); sSP(psp); sBP(pbp);
  sREG(rMAX,ml); sREG(rBEG,REGLEN);

  // Read
  { N1 bk=0,esc=0,str=0,cmt=0;
    N1 dig=0,num=0; N4 x=0,y=1;
    I4 blk=0; int ii; N1 cc;
  for (n1=REGLEN;n1<ml;)
  { ii=fgetc(stdin);
    if (ii==-1) break;
    else cc=(N1)ii;
    if (cc!='\n') ++x;
    else { ++y; x=0; }
    // Filter
    if (dig)
    { if ('0'<=cc && cc<='9') cc-='0'; else
      if ('A'<=cc && cc<='F') cc-='A'-10; else
      if (dig==1) ERR2("Error: (%lu,%lu): Num",y,x);
      else if (!(dig&1)) ERR2("Error: (%lu,%lu): Dig",y,x);
      else { dig=0; num=0; }
      if (dig)
      { if (dig&1)
        { num=cc; ++dig; }
        else
        { num=(num<<4)|cc; dig=3;
          mem[n1++]=num;
        }
        continue;
      }
    } else
    if (str)
    { if (esc)
      { esc=0; switch (cc)
        { case 'n':
            cc='\n'; break;
          case 'r':
            cc='\r'; break;
          case 't':
            cc='\t'; break;
        }
      } else
      { if (cc=='"')
        { str=0; continue; }
        if (cc=='\\')
        { esc=1; continue; }
      }
      mem[n1++]=cc;
      continue;
    } else
    if (cmt==1)
    { if (bk=='*' && cc=='/')
      { cmt=0; bk=0; continue; }
      bk=cc; continue;
    } else
    if (cmt==2)
    { if (cc=='\n') cmt=0;
      continue;
    }
    if (cc=='\\')
    { dig=1; bk=0; continue; }
    if (cc=='"')
    { str=1; bk=0; continue; }
    if (bk=='/')
    { if (cc=='*')
      { cmt=1; --n1; bk=0; continue; }
      if (cc=='/')
      { cmt=2; --n1; bk=0; continue; }
    }
    switch (cc)
    { case '\n':
      case '\r':
      case '\t':
      case ' ':
        bk=0; continue;
      case '{':
        if (++blk<0)
          ERR2("Error: (%lu,%lu): BLK1: Over",y,x);
        if (n1+6>ml)
          ERR2("Error: (%lu,%lu): BLK1: Mem",y,x);
        --psp; mStk(1,"BLK1/STK");
        *psp=mx4(n1);
        mem[n1++]='['; mem[n1++]='.';
        mem[n1++]='_'; mem[n1++]='_';
        mem[n1++]='_'; mem[n1++]='_';
        bk=0; continue;
      case '}':
        if (--blk<0)
          ERR2("Error: (%lu,%lu): BLK2: Under",y,x);
        mStk(1,"BLK2/STK");
        n2=mx4(*psp); ++psp; n3=n1-n2;
        if (n3>=0x10000ul)
          ERR2("Error: (%lu,%lu): BLK2: Len",y,x);
        { N1 *p1=mp1(mem,n2)+2;
        for (i2=12,i1=0;i1<4;++i1)
        { num=(n3>>i2)&0xF;
          if (num<10) num+='0';
          else num+='A'-10;
          *p1++=num; i2-=4;
        } }
        cc=']'; break;
    }
    mem[n1++]=bk=cc;
  } }
  if (n1==ml)
    ERR0("Error: Nomem: File");
  sREG(rLEN,n1-REGLEN);

  while (run)
  { flg=gFL; pip=gIP; psp=gSP; pbp=gBP;
    if (mMem(mem,pip,1))
      ERR1("Error: %08lX: IP",mDif(pip,mem));
    switch (*pip)
    { case '[':
        i1=gREG(rBLK)+1; sREG(rBLK,i1);
        if (i1<0)
          ERR1("Error: %08lX: BLK1: Over",mDif(pip,mem));
        --psp; mStk(1,"BLK1/STK");
        *psp=mx4(mDif(pip,mem));
        break;
      case ']':
        i1=gREG(rBLK)-1; sREG(rBLK,i1);
        if (i1<0)
          ERR1("Error: %08lX: BLK2: Under",mDif(pip,mem));
        mStk(2,"BLK2/STK"); psp+=2;
        break;
      case 'o':
        mStk(3,"CONT/STK");
        if (psp[0])
        { n1=gREG(rBLK);
          if (!n1--)
            ERR1("Error: %08lX: CONT",mDif(pip,mem));
          sREG(rBLK,n1);
          n1=mx4(psp[2]); psp+=3;
          pip=mp1(mem,n1)-1;
        } else psp+=1;
        break;
      case 'b':
        mStk(3,"BREAK/STK");
        if (psp[0])
        { n1=gREG(rBLK);
          if (!n1)
            ERR1("Error: %08lX: BREAK",mDif(pip,mem));
          n1=mx4(psp[2]); n1+=mx4(psp[1]);
          pip=mp1(mem,n1)-1;
        } else psp+=1;
        break;
      case '\n':
      case '\r':
      case '\t':
      case ' ':
      case ';': // NOP
        break;
      case '$': // SYS
        mStk(1,"SYS/STK");
        if (mx4(*psp)==3)
        { run=0; break; }
        if (sys(mem,ml,pip,&psp)) return (-1);
        break;
      case 'j': // JMP
        mStk(2,"JMP/STK");
        if (psp[1])
        { n1=mx4(*psp); n2=mDif(pip,mem);
          if (flg&fSGN) pip+=n1;
          else pip=mp1(mem,n1);
          if (mMem(mem,pip,1))
            ERR2("Error: %08lX: JMP: %08lX",
            n2,mDif(pip,mem));
          --pip;
        }
        psp+=2;
        break;
      case 'c': // CALL
        mStk(1,"CALL/STK");
        n1=mx4(*psp);
        n2=mDif(pip,mem);
        *psp=mx4(mDif(pip+1,mem));
        if (flg&fSGN) pip+=n1;
        else pip=mp1(mem,n1);
        if (mMem(mem,pip,1))
          ERR2("Error: %08lX: CALL: %08lX",
          n2,mDif(pip,mem));
        --pip;
        break;
      case '~': // SGN
        flg|=fFLG|fSGN;
        break;
      case 'G': // GET
        mStk(1,"GET/STK");
        n1=mx4(*psp);
        if (n1+4>ml)
          ERR2("Error: %08lX: GET: %08lX",
          mDif(pip,mem),n1);
        *psp=mn4(mem,n1);
        n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      case 'g': // GT1
        mStk(1,"GT1/STK");
        n1=mx4(*psp);
        if (n1+1>ml)
          ERR2("Error: %08lX: GT1: %08lX",
          mDif(pip,mem),n1);
        n1=mn1(mem,n1);
        *psp=mx4(n1);
        zzFLG; mFLzs(n1);
        break;
      case 'S': // SET
        mStk(2,"SET/STK");
        n2=mx4(*psp++); n1=*psp++; sSP(psp);
        if (n2+4>ml)
          ERR2("Error: %08lX: SET: %08lX",
          mDif(pip,mem),n2);
        mn4(mem,n2)=(N4)n1;
        flg=gFL; pip=gIP; psp=gSP; pbp=gBP;
        if (n2==rIP) --pip;
        break;
      case 's': // ST1
        mStk(2,"ST1/STK");
        n2=mx4(*psp++); n1=mx4(*psp++); sSP(psp);
        if (n2+1>ml)
          ERR2("Error: %08lX: ST1: %08lX",
          mDif(pip,mem),n2);
        mn1(mem,n2)=(N1)n1;
        flg=gFL; pip=gIP; psp=gSP; pbp=gBP;
        if (n2==rIP) --pip;
        break;
      case 'w': // GT2
        mStk(1,"GT2/STK");
        n1=mx4(*psp);
        if (flg&fSGN)
          n1+=mDif(pip,mem);
        if (n1+2>ml)
          ERR2("Error: %08lX: GT2: %08lX",
          mDif(pip,mem),n1);
        n1=mn2(mem,n1); n1=mx2(n1);
        *psp=mx4(n1);
        zzFLG; mFLzs(n1);
        break;
      case 'W': // ST2
        mStk(2,"ST2/STK");
        n2=mx4(*psp++); n1=mx4(*psp++); sSP(psp);
        if (flg&fSGN)
          n2+=mDif(pip,mem);
        if (n2+2>ml)
          ERR2("Error: %08lX: ST2: %08lX",
          mDif(pip,mem),n2);
        mn2(mem,n2)=mx2(n1);
        flg=gFL; pip=gIP; psp=gSP; pbp=gBP;
        if (n2==rIP) --pip;
        break;
      case 'p': // POP
        mStk(1,"POP/STK");
        ++psp; break;
      case 'm': // DUP
        --psp;
        mStk(2,"DUP/STK");
        psp[0]=psp[1];
        n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      case 'x': // XCH
        mStk(2,"XCH/STK");
        n1=psp[0];
        psp[0]=psp[1];
        psp[1]=n1;
        break;
      case 'i': // INT1
        mStk(1,"INT1/STK");
        n1=mx4(*psp);
        n1=(I4)(I1)n1;
        *psp=mx4(n1);
        zzFLG; mFLzs(n1);
        break;
      case 'I': // INT2
        mStk(1,"INT2/STK");
        n1=mx4(*psp);
        n1=(I4)(I2)n1;
        *psp=mx4(n1);
        zzFLG; mFLzs(n1);
        break;
      case '.': // ZRO
        --psp;
        mStk(1,"ZRO/STK");
        n1=*psp=0;
        zzFLG; mFLzs(n1);
        break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F': // NUM
        mStk(1,"NUM/STK");
        n1=mx4(*psp); n2=*pip;
        if ('0'<=n2 && n2<='9')
          n2-='0'; else n2-='A'-10;
        n1=(n1<<4)|n2; *psp=mx4(n1);
        break;
      case '+': // ADD
        mStk(2,"ADD/STK");
        n2=mx4(*psp++); n1=mx4(*psp);
        zzFLG; mFLo(n1^n2);
        *psp=mx4(n2=n1+n2);
        mFLzso(n2); mFLco(n2,n1);
        break;
      case '-': // SUB
        mStk(2,"SUB/STK");
        n2=mx4(*psp++); n1=mx4(*psp);
        zzFLG; mFLo(n1^n2);
        *psp=mx4(n2=n1-n2);
        mFLzso(n2); mFLco(n1,n2);
        break;
      case '*': // MUL
        mStk(2,"MUL/STK");
        n2=mx4(*psp++); n1=mx4(*psp);
        if (flg&fSGN)
          *psp=mx4(n1=Typ(I4,n1)*Typ(I4,n2));
        else
          *psp=mx4(n1=Typ(N4,n1)*Typ(N4,n2));
        zzFLG; mFLzs(n1);
        break;
      case '/': // DIV
        mStk(2,"DIV/STK");
        n2=mx4(psp[0]); n1=mx4(psp[1]); if (!n1)
          ERR1("Error: %08lX: Div 0",mDif(pip,mem));
        if (flg&fSGN)
        { psp[1]=mx4(Typ(I4,n1)/Typ(I4,n2));
          psp[0]=mx4(Typ(I4,n1)%Typ(I4,n2));
        } else
        { psp[1]=mx4(Typ(N4,n1)/Typ(N4,n2));
          psp[0]=mx4(Typ(N4,n1)%Typ(N4,n2));
        }
        n1=mx4(psp[1]);
        zzFLG; mFLzs(n1);
        break;
      case 'n': // NEG
        mStk(1,"NEG/STK");
        n1=-mx4(*psp);
        *psp=mx4(n1);
        zzFLG; mFLzs(n1);
        break;
      case '!': // NOT
        mStk(1,"NOT/STK");
        *psp=~*psp;
        n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      case '&': // AND
        mStk(2,"AND/STK");
        n2=*psp++; *psp&=n2;
        n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      case '|': // OR
        mStk(2,"OR/STK");
        n2=*psp++; *psp|=n2;
        n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      case '^': // XOR
        mStk(2,"XOR/STK");
        n2=*psp++; *psp^=n2;
        n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      case '<': // ROL
        mStk(2,"ROL/STK");
        n2=mx4(*psp++); n1=mx4(*psp);
        if (n2>32) n2=32;
        if (flg&fSGN)
        { n1=(n1<<n2)|(n1>>(32-n2));
          zzFLG; mFLzs(n1);
        } else
        { zzFLG; mFLo(n1);
          if (n2) mFLc(n1<<(n2-1));
          n1=n1<<n2; mFLzso(n1);
        }
        *psp=mx4(n1);
        break;
      case '>': // SAR
        mStk(2,"SAR/STK");
        n2=mx4(*psp++); n1=mx4(*psp);
        if (n2>32) n2=32;
        zzFLG; mFLo(n1);
        if (n2) mFLc(n1<<(32-n2));
        if (flg&fSGN)
          n1=Typ(I4,n1)>>n2;
        else
          n1=Typ(N4,n1)>>n2;
        mFLzso(n1);
        *psp=mx4(n1);
        break;
      case 'a': // ADR
        mStk(1,"ADR/STK");
        n1=mx4(*psp);
        n1=mDif(pbp+n1,mem);
        *psp=mx4(n1);
        zzFLG; mFLzs(n1);
        break;
      case 'v': // VAR
        mStk(1,"VAR/STK");
        n1=mx4(*psp);
        if (mMem(mem,pbp,n1))
          ERR2("Error: %08lX: VAR: %08lX",
          mDif(pip,mem),mDif(pbp+n1,mem));
        *psp=pbp[n1]; n1=mx4(*psp);
        zzFLG; mFLzs(n1);
        break;
      default:
        ERR2("Error: %08lX: Code: %02lX",mDif(pip,mem),(N4)*pip);
    }
    if (run)
    { ++pip;
      if (flg&fFLG) flg&=~fFLG;
      else flg&=~fSGN;
      cnt=gREG(rCNT)-1; sREG(rCNT,cnt);
    }
    sFL(flg); sIP(pip); sSP(psp); sBP(pbp);
  }
  dump(mem); MemRel(mem);
  fflush(stdout); return (0);
}

N4 dump(N1 *mem)
{ FILE *f1; N4 n1; N1 *p1;

  if (!(f1=fopen("./exec.mem","wb")))
    ERR0("Error: Dump: Open");
  p1=mp1(mem,REGLEN);
  n1=gREG(rLEN);
  if (fwrite(p1,n1,1,f1)!=1)
    ERR0("Error: Dump: Write");
  fclose(f1); return (0);
}
